/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#ifndef __MATH_PLANE_H__
#define __MATH_PLANE_H__


#define ON_EPSILON					0.1f

// Plane sides
#define	PLANESIDE_FRONT				1
#define	PLANESIDE_BACK				2
#define	PLANESIDE_ON				3
#define PLANESIDE_CROSS				3

// Plane types
#define	PLANETYPE_X					0
#define	PLANETYPE_Y					1
#define	PLANETYPE_Z					2
#define PLANETYPE_NONAXIAL			3

class odPlane {
public:
	odVec3			normal;
	float			dist;
	short			type;
	short			signBits;

					odPlane (void);
	explicit		odPlane (const odVec3 &normal, float dist);

	odPlane &		operator = (const odPlane &plane);
	bool			operator == (const odPlane &plane) const;
	bool			operator != (const odPlane &plane) const;

	odPlane			operator - () const;

	bool			Compare (const odPlane &plane) const;
	bool			Compare (const odPlane &plane, float epsilon = 0.0f) const;
	void			Set (const odVec3 &normal, float dist);
	bool			Set (const odVec3 &point1, const odVec3 &point2, const odVec3 &point3);
	void			SetType (void);
	void			SetSignBits (void);
	float			Normalize (void);
	float			Distance (const odVec3 &vec) const;
	int				BoundsOnSide (const odBounds &bounds) const;
	int				SphereOnSide (const odVec3 &center, float radius) const;
	int				PointOnSide (const odVec3 &point, float epsilon = 0.0f) const;

	odVec4			ToVec4 (void) const;
	void			FromVec4 (const odVec4 &vec);

	const float *	ToFloatPtr (void) const;
	float *			ToFloatPtr (void);

	const char *	ToString (int precision = 2) const;
};

INLINE odPlane::odPlane (void){

}

INLINE odPlane::odPlane (const odVec3 &normal, float dist){

	this->normal = normal;
	this->dist = dist;
}

INLINE odPlane &odPlane::operator = (const odPlane &plane){

	normal = plane.normal;
	dist = plane.dist;
	type = plane.type;
	signBits = plane.signBits;

	return *this;
}

INLINE bool odPlane::operator == (const odPlane &plane) const {

	if (normal != plane.normal || dist != plane.dist)
		return false;

	return true;
}

INLINE bool odPlane::operator != (const odPlane &plane) const {

	if (normal == plane.normal && dist == plane.dist)
		return false;

	return true;
}

INLINE odPlane odPlane::operator - () const {

	return odPlane(-normal, -dist);
}

INLINE bool odPlane::Compare (const odPlane &plane) const {

	if (normal.x != plane.normal.x || normal.y != plane.normal.y || normal.z != plane.normal.z)
		return false;
	if (dist != plane.dist)
		return false;

	return true;
}

INLINE bool odPlane::Compare (const odPlane &plane, float epsilon) const {

	if (M_Fabs(normal.x - plane.normal.x) > epsilon)
		return false;
	if (M_Fabs(normal.y - plane.normal.y) > epsilon)
		return false;
	if (M_Fabs(normal.z - plane.normal.z) > epsilon)
		return false;
	if (M_Fabs(dist - plane.dist) > epsilon)
		return false;

	return true;
}

INLINE void odPlane::Set (const odVec3 &normal, float dist){

	this->normal = normal;
	this->dist = dist;
}

INLINE bool odPlane::Set (const odVec3 &point1, const odVec3 &point2, const odVec3 &point3){

	normal.Cross(point3 - point1, point2 - point1);

	if (!normal.Normalize()){
		dist = 0.0f;
		return false;
	}

	dist = point1.Dot(normal);

	return true;
}

INLINE void odPlane::SetType (void){

	if (normal.x == 1.0f){
		type = PLANETYPE_X;
		return;
	}
	if (normal.y == 1.0f){
		type = PLANETYPE_Y;
		return;
	}
	if (normal.z == 1.0f){
		type = PLANETYPE_Z;
		return;
	}

	type = PLANETYPE_NONAXIAL;
}

INLINE void odPlane::SetSignBits (void){

	signBits = 0;

	if (normal.x < 0.0f)
		signBits |= BIT(0);
	if (normal.y < 0.0f)
		signBits |= BIT(1);
	if (normal.z < 0.0f)
		signBits |= BIT(2);
}

INLINE float odPlane::Normalize (void){

	float	length, invLength;

	length = normal.Length();

	if (length){
		invLength = 1.0f / length;

		normal *= invLength;
		dist *= invLength;
	}

	return length;
}

INLINE float odPlane::Distance (const odVec3 &vec) const {

	if (type < PLANETYPE_NONAXIAL)
		return vec[type] - dist;

	return vec.Dot(normal) - dist;
}

INLINE const float *odPlane::ToFloatPtr (void) const {

	return &normal.x;
}

INLINE float *odPlane::ToFloatPtr (void) {

	return &normal.x;
}


#endif	// __MATH_PLANE_H__
